#ifdef WITH_CG

#include "shader_cg.h"
#include "shader_catalog_cg.h"
#include <iostream>

using namespace krryn::system_imp;
using namespace krryn::view;

shader_cg::shader_cg(shader_catalog_cg &a_Catalog, const std::string &a_Filename, CGcontext a_Context)
	: m_Filename(a_Filename), m_Context(a_Context), shader(a_Catalog), m_CompileError(false)
{
	m_Params.resize(shader_type_count);
}

void shader_cg::init(std::string a_Filename){
	a_Filename.append(".cg");

	std::clog << "Compiling shader '" << a_Filename << "'" << std::endl;

	// cgGetProfileString returns NULL if the profile is invalid.
	// there is no 'proper' way to test if a profile is valid.
	if(cgGetProfileString(m_VertexProfile) == NULL){
		std::clog << "Must initialize vertex profile before init()" << std::endl;
	}

	if(cgGetProfileString(m_PixelProfile) == NULL){
		std::clog << "Must initialize pixel profile before init()" << std::endl;
	}

	m_VertexShader = cgCreateProgramFromFile(
		m_Context, 
		CG_SOURCE,
		a_Filename.c_str(),
		m_VertexProfile,
		"main_vs", NULL);
	check_error(m_Context);

	m_PixelShader = cgCreateProgramFromFile(
		m_Context,
		CG_SOURCE,
		a_Filename.c_str(),
		m_PixelProfile,
		"main_ps", NULL);

	//assert(cgIsProgram(m_VertexShader) && "Invalid vertex shader");
	//assert(cgIsProgram(m_PixelShader) && "Invalid pixel shader");

	check_error(m_Context);

	load_params(vs, m_VertexShader);
	load_params(ps, m_PixelShader);
}

std::string shader_cg::get_source(shader_type a_Type){
	return cgGetProgramString(a_Type == vs ? m_VertexShader : m_PixelShader, CG_PROGRAM_SOURCE);
}

std::string shader_cg::get_compiled_source(shader_type a_Type){
	CGprogram l_Program = a_Type == vs ? m_VertexShader : m_PixelShader;

	if(!cgIsProgramCompiled(l_Program))
		cgCompileProgram(l_Program);

	return cgGetProgramString(l_Program, CG_COMPILED_PROGRAM);
}

void shader_cg::load_params(shader_type a_Type, CGprogram a_Program){
	CGparameter l_Param = cgGetFirstParameter(a_Program, CG_PROGRAM);
	while(l_Param){
		m_Params[a_Type][cgGetParameterName(l_Param)] = l_Param;
		l_Param = cgGetNextParameter(l_Param);
	}
}

void shader_cg::throw_shader_param_error(shader_type a_Type, const std::string &a_Name){
	// Only send out parameter errors when the shader compiles and isn't an error handler
	// that way we avoid errors when set_* functions are called on the with the assumption
	// that the shader compiled. 
	// Alternative interpretation: only call when the parameter is actually 
	// missing in a working shader
	if(!m_CompileError && !m_Catalog.is_error_shader(*this)){
		std::string l_ShaderType = (a_Type == vs) ? "vertex shader" : "pixel shader";

		std::string l_Error = "Krryn: Invalid shader parameter: '" + a_Name + 
			"' in " + l_ShaderType + " '" + m_Filename + "'.", 
			shader_parameter_error;

		compile_error_args l_Args;
		l_Args.m_ErrorMessage = l_Error;
		m_Catalog.m_OnCompileError.trigger(&l_Args);
	}
}

shader_cg::~shader_cg(){
	cgDestroyProgram(m_VertexShader);
	cgDestroyProgram(m_PixelShader);
}

void shader_cg::check_error(CGcontext cgContext){
	CGerror l_Error = cgGetFirstError();
	while(l_Error != CG_NO_ERROR){
		const char* l_String = cgGetErrorString(l_Error);
		if (l_Error == CG_COMPILER_ERROR){
			std::string l_Listing = cgGetLastListing(cgContext);
			std::clog << l_Listing << std::endl;

			m_CompileError = true;

			throw shader_error(l_Listing, shader_compile_error);
		}else if(l_Error == CG_INVALID_PROGRAM_HANDLE_ERROR){
			if(!cgIsProgram(m_VertexShader)){
				std::clog << "Invalid vertex shader" << std::endl;
			}else if(!cgIsProgram(m_PixelShader)){
				std::clog << "Invalid pixel shader" << std::endl;
			}
			std::clog << l_String << std::endl;
		}else if(l_Error != CG_NO_ERROR){
			std::clog << l_String << std::endl;

//			throw shader_error(l_String, shader_warning);
		}

		l_Error = cgGetFirstError();
	}
}

#endif